import tkinter as tk
from tkinter import scrolledtext, ttk
import threading
import queue
import os
import zipfile
import schedule
import time
import re
from datetime import datetime
import configparser

class FileCompressor:
    def __init__(self, root):
        self.root = root
        self.root.title("数据库文件压缩工具 著作:MrCui")
        self.root.geometry("480x650")
        self.root.geometry("+%d+%d" % (root.winfo_screenwidth() // 2 - 290, root.winfo_screenheight() // 2 - 325))
        
        # 初始化样式
        self.style = ttk.Style(root)
        self.style.theme_use('default')
        
        # 日志配置
        self.log_dir = r"C:\FilesLogs"
        self.log_file = os.path.join(self.log_dir, "app.log")
        self.config_file = os.path.join(self.log_dir, "config.ini")
        self.log_queue = queue.Queue()
        self.current_row = 0
        
        # 任务控制
        self.scheduler_running = False
        self.schedule_thread = None
        
        # 初始化日志系统
        self.init_log_system()
        self.create_widgets()
        # 加载配置，移到 create_widgets 之后
        self.load_config()
        self.root.after(100, self.process_log_queue)

    def init_log_system(self):
        """初始化日志系统"""
        try:
            # 创建日志目录（如果不存在）
            os.makedirs(self.log_dir, exist_ok=True)

            # 创建/清空日志文件（如果不存在）
            if not os.path.exists(self.log_file):
                with open(self.log_file, 'w') as f:
                    f.write("")
                self.insert_log(f"已创建日志文件：{self.log_file}")

            self.insert_log("========== 应用程序启动 著作:MrCui ==========")
        except Exception as e:
            self.log_queue.put(f"日志系统初始化失败：{str(e)}")

    def write_to_logfile(self, message):
        """写入日志文件"""
        try:
            timestamp = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
            with open(self.log_file, 'a', encoding='utf-8') as f:
                f.write(f"[{timestamp}] {message}\n")
        except Exception as e:
            self.log_queue.put(f"写入日志文件失败：{str(e)}")

    def create_widgets(self):
        """创建界面组件"""
        # 标题
        title_label = tk.Label(self.root,
                               text="数据库文件压缩工具",
                               font=("楷体", 12, "bold"),
                               fg="red")
        title_label.pack(side=tk.TOP, fill=tk.X, pady=(0, 0))

        font_style = ("楷体", 12)
        main_frame = tk.Frame(self.root, padx=20, pady=20)
        main_frame.pack(fill=tk.BOTH, expand=True)

        # 日志路径显示
        log_path_frame = tk.LabelFrame(main_frame,
                                       text="日志存储路径",
                                       font=font_style,
                                       padx=10,
                                       pady=10)
        log_path_frame.pack(side=tk.TOP, fill=tk.X, pady=(0, 10))

        self.log_path_var = tk.StringVar(value=self.log_file)
        tk.Label(log_path_frame, text="日志文件:", font=font_style).grid(row=0, column=0, sticky=tk.W, pady=5)
        log_entry = tk.Entry(log_path_frame,
                             textvariable=self.log_path_var,
                             width=50,
                             font=font_style,
                             state='readonly')
        log_entry.grid(row=0, column=1, padx=10, pady=5, sticky=tk.E + tk.W)

        # 文件路径输入区
        input_frame = tk.LabelFrame(main_frame,
                                    text="需要压缩文件的信息",
                                    font=font_style,
                                    padx=10,
                                    pady=10)
        input_frame.pack(side=tk.TOP, fill=tk.X, pady=(0, 10))

        tk.Label(input_frame, text="文件路径:", font=font_style).grid(row=0, column=0, sticky=tk.W, pady=5)
        self.source_path_entry = tk.Entry(input_frame, width=40, font=font_style)
        self.source_path_entry.grid(row=0, column=1, padx=10, pady=5, sticky=tk.E + tk.W)

        tk.Label(input_frame, text="文件类型:", font=font_style).grid(row=1, column=0, sticky=tk.W, pady=5)
        file_types = [".bak", ".sql", ".db", ".mdf", ".ldf"]
        self.file_type_combobox = ttk.Combobox(input_frame, values=file_types, width=37, font=font_style)
        self.file_type_combobox.set(file_types[0])
        self.file_type_combobox.grid(row=1, column=1, padx=10, pady=5, sticky=tk.E + tk.W)

        # 输出路径设置
        output_frame = tk.LabelFrame(main_frame,
                                     text="压缩后文件的存放信息",
                                     font=font_style,
                                     padx=10,
                                     pady=10)
        output_frame.pack(side=tk.TOP, fill=tk.X, pady=(0, 10))

        tk.Label(output_frame, text="存放路径:", font=font_style).grid(row=0, column=0, sticky=tk.W, pady=5)
        self.destination_path_entry = tk.Entry(output_frame, width=40, font=font_style)
        self.destination_path_entry.grid(row=0, column=1, padx=10, pady=5, sticky=tk.E + tk.W)

        # 定时设置区
        timer_frame = tk.LabelFrame(main_frame,
                                    text="定时器设置",
                                    font=font_style,
                                    padx=10,
                                    pady=10)
        timer_frame.pack(side=tk.TOP, fill=tk.X, pady=(0, 10))

        tk.Label(timer_frame, text="执行频率:", font=font_style).grid(row=0, column=0, sticky=tk.W, pady=5)
        frequencies = ["每天", "每月"]
        self.frequency_combobox = ttk.Combobox(timer_frame, values=frequencies, width=37, font=font_style)
        self.frequency_combobox.set(frequencies[0])
        self.frequency_combobox.grid(row=0, column=1, padx=10, pady=5, sticky=tk.E + tk.W)

        tk.Label(timer_frame, text="执行时间段:", font=font_style).grid(row=1, column=0, sticky=tk.W, pady=5)
        self.time_var = tk.StringVar(value="00:00")
        self.time_entry = tk.Entry(timer_frame, textvariable=self.time_var, width=10, font=font_style)
        self.time_entry.grid(row=1, column=1, padx=10, pady=5, sticky=tk.W)
        self.time_entry.bind("<FocusOut>", self.validate_time_format)

        # 操作选项区
        action_frame = tk.LabelFrame(main_frame,
                                     text="执行后的操作",
                                     font=font_style,
                                     padx=10,
                                     pady=10)
        action_frame.pack(side=tk.TOP, fill=tk.X, pady=(0, 10))

        tk.Label(action_frame, text="操作选择:", font=font_style).grid(row=0, column=0, sticky=tk.W, pady=5)
        actions = ["删除源文件", "不删除源文件"]
        self.action_combobox = ttk.Combobox(action_frame, values=actions, width=37, font=font_style)
        self.action_combobox.set(actions[1])
        self.action_combobox.grid(row=0, column=1, padx=10, pady=5, sticky=tk.E + tk.W)

        # 控制按钮区
        button_frame = tk.Frame(main_frame)
        button_frame.pack(side=tk.TOP, fill=tk.X, pady=(0, 10))

        self.start_button = tk.Button(button_frame,
                                      text="开始",
                                      command=self.start_compression,
                                      font=font_style,
                                      width=10)
        self.start_button.pack(side=tk.LEFT, padx=10)

        self.stop_button = tk.Button(button_frame,
                                     text="结束",
                                     command=self.stop_compression,
                                     font=font_style,
                                     width=10,
                                     state=tk.DISABLED)
        self.stop_button.pack(side=tk.LEFT, padx=10)

        # 日志显示区
        log_frame = tk.LabelFrame(main_frame,
                                  text="操作日志",
                                  font=font_style,
                                  padx=10,
                                  pady=10)
        log_frame.pack(side=tk.TOP, fill=tk.BOTH, expand=True)

        self.text_box = scrolledtext.ScrolledText(log_frame, width=80, height=20, font=font_style)
        self.text_box.pack(fill=tk.BOTH, expand=True)

        self.row_label = tk.Label(main_frame, text="当前执行行数: 0", font=font_style)
        self.row_label.pack(side=tk.BOTTOM, pady=5)

    def validate_time_format(self, event):
        """验证时间格式"""
        time_str = self.time_var.get()
        pattern = r"^(?:[01]\d|2[0-3]):[0-5]\d$"
        if not re.match(pattern, time_str):
            self.insert_log("时间格式不正确，请输入 HH:MM 格式的时间", error=True)
            self.time_var.set("00:00")

    def toggle_controls(self, state):
        """切换控件状态"""
        controls = [
            self.source_path_entry,
            self.file_type_combobox,
            self.destination_path_entry,
            self.frequency_combobox,
            self.time_entry,
            self.action_combobox,
            self.start_button
        ]

        for control in controls:
            if isinstance(control, ttk.Combobox):
                control["state"] = "readonly" if state == tk.NORMAL else "disabled"
            else:
                control["state"] = state

        self.stop_button["state"] = tk.NORMAL if state == tk.DISABLED else tk.DISABLED

    def insert_log(self, message, error=False):
        """记录日志"""
        timestamp = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
        log_message = f"[{timestamp}] {message}"

        # 写入队列和文件
        self.log_queue.put(log_message)
        self.write_to_logfile(log_message)

    def process_log_queue(self):
        """处理日志队列"""
        try:
            while True:
                message = self.log_queue.get_nowait()
                self.text_box.insert(tk.END, f"{message}\n")
                self.text_box.see(tk.END)
                self.current_row += 1
                self.row_label.config(text=f"当前执行行数: {self.current_row}")
        except queue.Empty:
            pass
        self.root.after(100, self.process_log_queue)

    def compress_files(self):
        """执行压缩操作"""
        try:
            source_path = self.source_path_entry.get()
            file_type = self.file_type_combobox.get()
            destination_path = self.destination_path_entry.get()
            action = self.action_combobox.get()

            # 参数验证
            if not all([source_path, file_type, destination_path]):
                self.insert_log("错误：请填写所有必要字段", error=True)
                return

            if not os.path.exists(source_path):
                self.insert_log(f"错误：源路径不存在 - {source_path}", error=True)
                return

            # 创建目标目录
            if not os.path.exists(destination_path):
                try:
                    os.makedirs(destination_path)
                    self.insert_log(f"已创建目标目录：{destination_path}")
                except Exception as e:
                    self.insert_log(f"创建目标目录失败：{str(e)}", error=True)
                    return

            # 执行压缩
            self.insert_log("开始文件扫描...")
            file_count = 0
            for root_dir, _, files in os.walk(source_path):
                for file in files:
                    if file.endswith(file_type):
                        file_path = os.path.join(root_dir, file)
                        zip_name = f"{os.path.splitext(file)[0]}.zip"
                        zip_path = os.path.join(destination_path, zip_name)

                        try:
                            with zipfile.ZipFile(zip_path, 'w', zipfile.ZIP_DEFLATED) as zipf:
                                zipf.write(file_path, arcname=file)
                            self.insert_log(f"成功压缩：{file} → {zip_name}")

                            if action == "删除源文件":
                                os.remove(file_path)
                                self.insert_log(f"已删除源文件：{file}")

                            file_count += 1
                        except Exception as e:
                            self.insert_log(f"压缩失败：{file} - {str(e)}", error=True)

            self.insert_log(f"操作完成，共处理 {file_count} 个文件")

        except Exception as e:
            self.insert_log(f"发生未预期的错误：{str(e)}", error=True)

    def start_compression(self):
        """启动定时任务"""
        # 验证时间格式
        time_str = self.time_var.get()
        if not re.match(r"^(?:[01]\d|2[0-3]):[0-5]\d$", time_str):
            self.insert_log("错误：无效的时间格式（HH:MM）", error=True)
            return

        # 禁用控件
        self.toggle_controls(tk.DISABLED)
        self.scheduler_running = True

        # 设置定时任务
        frequency = self.frequency_combobox.get()
        try:
            if frequency == "每天":
                schedule.every().day.at(time_str).do(self.compress_files)
            elif frequency == "每月":
                # schedule 库没有直接支持每月的 at 方法，这里简单提示不支持
                self.insert_log("错误：不支持每月定时任务，仅支持每天定时任务", error=True)
                self.toggle_controls(tk.NORMAL)
                return
            else:
                raise ValueError("未知的执行频率")
        except Exception as e:
            self.insert_log(f"定时任务设置失败：{str(e)}", error=True)
            self.toggle_controls(tk.NORMAL)
            return

        # 保存配置
        self.save_config()

        # 启动调度线程
        def schedule_loop():
            while self.scheduler_running:
                schedule.run_pending()
                time.sleep(1)

        self.schedule_thread = threading.Thread(target=schedule_loop, daemon=True)
        self.schedule_thread.start()
        self.insert_log(f"定时任务已启动 - {frequency} {time_str}")

    def stop_compression(self):
        """停止定时任务"""
        self.scheduler_running = False
        schedule.clear()
        self.toggle_controls(tk.NORMAL)
        self.insert_log("定时任务已停止")

    def load_config(self):
        """加载配置文件"""
        if os.path.exists(self.config_file):
            config = configparser.ConfigParser()
            config.read(self.config_file)
            if 'Settings' in config:
                settings = config['Settings']
                self.source_path_entry.insert(0, settings.get('source_path', ''))
                self.file_type_combobox.set(settings.get('file_type', '.bak'))
                self.destination_path_entry.insert(0, settings.get('destination_path', ''))
                self.frequency_combobox.set(settings.get('frequency', '每天'))
                self.time_var.set(settings.get('time', '00:00'))
                self.action_combobox.set(settings.get('action', '不删除源文件'))

    def save_config(self):
        """保存配置文件"""
        config = configparser.ConfigParser()
        config['Settings'] = {
            'source_path': self.source_path_entry.get(),
            'file_type': self.file_type_combobox.get(),
            'destination_path': self.destination_path_entry.get(),
            'frequency': self.frequency_combobox.get(),
            'time': self.time_var.get(),
            'action': self.action_combobox.get()
        }
        with open(self.config_file, 'w') as configfile:
            config.write(configfile)

if __name__ == "__main__":
    root = tk.Tk()
    app = FileCompressor(root)
    root.mainloop()
